<?php

/**
 * 计数器限流 用Hash实现
 * Class CounterLimit
 */
class CounterLimit
{
    // 限制最多跑多少次
    const LIMIT_COUNT = 100;
    // 多少时间间隔
    const LIMIT_UNIT = 60;
    // REDIS Hash的键
    const LIMIT_KEY = 'limit:counter';
    // 过期时间
    const TTL = 120;

    // Redis 实例
    private $redis;

    public function __construct()
    {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }

    /**
     * 检测请求是否可以通过
     *
     * @param $uid
     * @return bool
     */
    public function checkRequest($uid)
    {
        $countData = $this->redis->hGet(self::LIMIT_KEY, $uid);

        if ($countData) {
            // 持续请求
            $countValue = json_decode($countData, true);

            // 算时间戳，用于判断是否过了一分钟了
            $limitTime = time() - self::LIMIT_UNIT;
            if ($countValue['time'] < $limitTime) {
                // 之前就已经超了
                if ($countValue['count'] > self::LIMIT_COUNT) {
                    // 超了还请求？继续设置过期时间
                    $this->redis->expire(self::LIMIT_KEY, self::TTL);
                    return false;
                } else {
                    // 没超可以初始化了
                    $initData = $this->updateUserCountData();
                    $this->setLimitData($uid, $initData);
                    return true;
                }
            } else {
                // 一分钟以内
                if ($countValue['count'] >= self::LIMIT_COUNT) {
                    // 超了
                    $this->redis->expire(self::LIMIT_KEY, self::TTL);
                    return false;
                } else {
                    // 没超
                    $countValue['count'] = $countValue['count'] + 1;
                    $this->setLimitData($uid, json_encode($countValue));
                    return true;
                }
            }
        } else {
            // 第一次请求，或者间隔一分钟的请求
            $initData = $this->updateUserCountData();
            $this->setLimitData($uid, $initData);
            return true;
        }
    }

    /**
     * 设置限制数据
     *
     * @param $uid
     * @param $data
     */
    private function setLimitData($uid, $data)
    {
        $this->redis->hSet(self::LIMIT_KEY, $uid, $data);
        $this->redis->expire(self::LIMIT_KEY, self::TTL);
    }

    /**
     * 更新用户限制数据
     *
     * @return false|string
     */
    private function updateUserCountData()
    {
        $data = [
            'time'  => time(),
            'count' => 1
        ];
        return json_encode($data);
    }
}
